package pacman;

import java.io.BufferedReader;
import java.io.DataInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.regex.Pattern;

import maze.Direction;
import maze.Location;
import maze.Maze;

public class CommandParser {

	private final boolean VERBOSE = true;

	private Game game;

	public CommandParser(Game game) {
		this.setGame(game);
	}

	/**
	 * Parses all the info from a maze-file. Initializes the maze, sets the
	 * players on the possible positions and sets pacman on the given position
	 */
	public Maze parseMaze(File file) throws IOException {
		Maze maze = getMaze(file);
		givePlayersPositions(file, maze);
		givePacmanPosition(file);
		addBarcodes(file, maze);
		return maze;
	}

	/**
	 * Reads the maze.txt file and uses the BARCODE command to add barcodes
	 */
	private void addBarcodes(File file, Maze maze) throws IOException {
		FileInputStream fstream = new FileInputStream(file);
		DataInputStream in = new DataInputStream(fstream);
		BufferedReader br = new BufferedReader(new InputStreamReader(in));
		String strLine;
		while ((strLine = br.readLine()) != null) {
			String[] parts = partitionStringFile(strLine);
			if (parts[1].equals("BARCODEAT")) {
				String bc = Integer.toBinaryString(Integer.parseInt(parts[3]));
				int[] coords = getCoordsFromString(parts[2]);
				;
				int direction = Integer.parseInt(parts[4]);
				if (direction == 3 || direction == 2) { // String reversen als
														// ze niet in de richten
														// zijn
					StringBuffer reverser = new StringBuffer(bc); // Die
																	// overeenkomt
																	// zoals
																	// nodig
																	// voor
																	// addBarcode
					bc = new String(reverser.reverse());
				}
				while (bc.length() < 8) {
					bc = 0 + bc;
				}
				if (direction == 1 || direction == 3) { // Strings zijn al
														// gereversed dus voor
														// verticale richtingen
					maze.addBarcode(coords[0], coords[1], bc, 1);
				} else {
					maze.addBarcode(coords[0], coords[1], bc, 0);
				}
			}
		}
	}

	/**
	 * Reads the maze.txt file and uses the PACMAN command to give Pacman it's
	 * starting position
	 */
	private void givePacmanPosition(File file) throws IOException {
		FileInputStream fstream = new FileInputStream(file);
		DataInputStream in = new DataInputStream(fstream);
		BufferedReader br = new BufferedReader(new InputStreamReader(in));
		String strLine;
		while ((strLine = br.readLine()) != null) {
			String[] parts = partitionStringFile(strLine);
			if (parts[1].equals("PACMAN")) {
				int[] coords = getCoordsFromString(parts[2]);
				getGame().getPacman().setLocation(new Location(coords[0] * 40 + 20, coords[1] * 40 + 20));
			}
		}
	}

	/**
	 * Reads the maze.txt file and uses the POSITION commands to create the new
	 * players and give them their starting positions in the new maze.
	 */
	private void givePlayersPositions(File file, Maze maze) throws IOException {
		FileInputStream fstream = new FileInputStream(file);
		DataInputStream in = new DataInputStream(fstream);
		BufferedReader br = new BufferedReader(new InputStreamReader(in));
		String strLine;
		int playersWithPositions = 0;
		while ((strLine = br.readLine()) != null) {
			System.out.println(strLine);
			String[] parts = partitionStringFile(strLine);
			if (parts[1].equals("POSITION")) {
				int[] coords = getCoordsFromString(parts[2]);
				getGame().getPositions()[playersWithPositions][0] = coords[0] * 40 + 20; // +20
																							// to
																							// get
																							// in
																							// middle
																							// of
																							// sector
				getGame().getPositions()[playersWithPositions][1] = coords[1] * 40 + 20;
				playersWithPositions++;
			}
		}
	}

	/**
	 * Reads the maze.txt file and uses the DISCOVER commands to create a maze
	 */
	private Maze getMaze(File file) throws NumberFormatException, IOException {
		FileInputStream fstream = new FileInputStream(file);
		DataInputStream in = new DataInputStream(fstream);
		BufferedReader br = new BufferedReader(new InputStreamReader(in));
		String strLine;
		Maze maze = new Maze(6);
		while ((strLine = br.readLine()) != null) {
			System.out.println(strLine);
			String[] parts = partitionStringFile(strLine);
			if (parts[1].equals("DISCOVER")) {
				int[] coords = getCoordsFromString(parts[2]);
				int n = Integer.parseInt(parts[3]);
				maze.write(coords[0], coords[1], Direction.NORTH, (char) (2 - n), true);
				int o = Integer.parseInt(parts[4]);
				maze.write(coords[0], coords[1], Direction.EAST, (char) (2 - o), true);
				int z = Integer.parseInt(parts[5]);
				maze.write(coords[0], coords[1], Direction.SOUTH, (char) (2 - z), true);
				int w = Integer.parseInt(parts[6]);
				maze.write(coords[0], coords[1], Direction.WEST, (char) (2 - w), true);
			}
		}
		in.close();
		return maze;
	}

	public void parseCommand(String command) throws IOException {
		String[] parts = partitionStringCommand(command);
		if (parts[0].equals("JOIN")) {
			game.processJOIN();
			if (VERBOSE) {
				System.out.println("JOIN");
			}

		} else if (parts[1].equals("NAME")) {
			game.processNAME(parts[0], // Name
					parts[2]); // Version
			if (VERBOSE) {
				System.out.println("NAME\n" + "Name: " + parts[0] + "\nVersion: " + parts[2]);
			}
		} else if (parts[1].equals("POSITION")) {
			int[] coords = getCoordsFromString(parts[2]);
			game.processPOSITION(parts[0], // Name
					coords[0], // X
					coords[1]); // Y
			if (VERBOSE) {
				System.out.println("POSITION\n" + "Name: " + parts[0] + "\nX: " + coords[0] + "\nY: " + coords[1]);
			}

		} else if (parts[1].equals("DISCOVER")) {
			int[] coords = getCoordsFromString(parts[2]);
			game.processDISCOVER(parts[0], // Name
					coords[0], // X
					coords[1], // Y
					Integer.parseInt(parts[3]), // NORTH
					Integer.parseInt(parts[4]), // EAST
					Integer.parseInt(parts[5]), // SOUTH
					Integer.parseInt(parts[6])); // WEST
			if (VERBOSE) {
				System.out.println("DISCOVER\n" + "Name: " + parts[0] + "\nX: " + coords[0] + "\nY: " + coords[1] + "\nNORTH: " + Integer.parseInt(parts[3]) + "\nEAST: " + Integer.parseInt(parts[4])
						+ "\nSOUTH: " + Integer.parseInt(parts[5]) + "\nWEST: " + Integer.parseInt(parts[6]));
			}

		} else if (parts[1].equals("BARCODE")) {
			game.processBARCODE(parts[0], // Name
					Integer.parseInt(parts[2]), // Code
					Integer.parseInt(parts[3])); // Direction
			if (VERBOSE) {
				System.out.println("BARCODE\n" + "Name: " + parts[0] + "\nCode: " + Integer.parseInt(parts[2]) + "\nDirection: " + Integer.parseInt(parts[3]));
			}

		} else if (parts[1].equals("PACMAN")) {
			int[] coords = getCoordsFromString(parts[2]);
			game.processPACMAN(coords[0], // X
					coords[1]); // Y
			if (VERBOSE) {
				System.out.println("PACMAN\n" + "X: " + coords[0] + "\nY: " + coords[1]);
			}

		} else if (parts[1].equals("CAPTURED")) {
			game.processCAPTURED(parts[0]); // Name
			if (VERBOSE) {
				System.out.println("CAPTURED\n" + "Name: " + parts[0]);
			}

		} else if (parts[1].equals("PLAN")) {
			ArrayList<Location> plan = new ArrayList<Location>();
			for (int i = 2; i < parts.length; i++) {
				int[] coords = getCoordsFromString(parts[i]);
				plan.add(new Location(coords[0], coords[1]));
			}
			game.processPLAN(parts[0], plan);
			if (VERBOSE) {
				System.out.println("PLAN\n" + "Name: " + parts[0]);
				for (Location loc : plan) {
					System.out.println("Step " + plan.indexOf(loc) + ": " + loc.toString());
				}
			}

		} else if (parts[1].equals("CANCELPLAN")) {
			game.processCANCELPLAN(parts[0]);
			if (VERBOSE) {
				System.out.println("CANCELPLAN\n" + "Name: " + parts[0]);
			}

		} else if (parts[1].equals("PING")) {
			game.processPING(parts[0], // Name
					parts[2], // Destination
					parts[3]); // Message
			if (VERBOSE) {
				System.out.println("PING\n" + "Name: " + parts[0] + "\nDestination: " + parts[2] + "\nMessage: " + parts[3]);
			}

		} else if (parts[1].equals("PONG")) {
			game.processPONG(parts[2], // Destination
					parts[3]); // Message
			if (VERBOSE) {
				System.out.println("PONG\n" + "Destination: " + parts[2] + "\nMessage: " + parts[3]);
			}
		}
	}

	private int[] getCoordsFromString(String coord) {
		String[] coords = Pattern.compile(",").split(coord);
		int[] result = new int[2];
		result[0] = Integer.parseInt(coords[0]);
		result[1] = Integer.parseInt(coords[1]);
		return result;
	}

	/**
	 * Partitions a string received by RabbitMQ. Standard delimiter is " "
	 */
	private String[] partitionStringCommand(String input) {
		input = (String) input.substring(0, input.length() - 1); // \n op einde
																	// verwijderen
		String[] parts = Pattern.compile(" +").split(input);
		return parts;
	}

	/**
	 * Partitions a line from a txt file. Standard delimiter is " "
	 */
	private String[] partitionStringFile(String input) {
		input = (String) input.substring(0, input.length()); // \n op einde
																// verwijderen
		String[] parts = Pattern.compile(" +").split(input);
		return parts;
	}

	public static void main(String[] args) throws IOException {

	}

	public Game getGame() {
		return game;
	}

	private void setGame(Game game) {
		this.game = game;
	}
}
